# autoencoder.py
#
import numpy as np
import torch
import torch.nn as nn
import torch.utils.data as Data
import torchvision
import matplotlib.pyplot as plt

'''
MNIST samples

+ train-images-idx3-ubyte.gz (training set images, 包含 60,000 个样本) shape = (60000, 28, 28)
+ train-labels-idx1-ubyte.gz (training set labels, 包含 60,000 个标签) shape = (60000, 1)
+ t10k-images-idx3-ubyte.gz (test set images, 包含 10,000 个样本)
+ t10k-labels-idx1-ubyte.gz (test set labels, 包含 10,000 个标签)
'''

# 超参数
ALLSAMPLES = 60000  # 样本总数
EPOCHS = 3          # 轮次
BATCHS = 1000       # 批次
BATCH_SIZE = 60     # 每批样本数量 ALLSAMPLES = BATCH_SIZE * BATCHS
LR = 0.005          # 学习率 learning rate
N_TEST_IMG = 5      # 图片数量

DOWNLOAD_MNIST = False

MNIST_ROOT_DIR = 'e:/sfxData/DeepLearning'

# 下载数据:
train_data = torchvision.datasets.MNIST(
    root=MNIST_ROOT_DIR,
    train=True,
    transform=torchvision.transforms.ToTensor(),
    download=DOWNLOAD_MNIST,
)

# 小批数据
# 有 60000 训练样本，
# 分 1000 批次进入网络模型，
# 每批次样本数 BATCH_SIZE = 60
train_loader = Data.DataLoader(
    dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)

# 搭建网络


class AutoEncoder(nn.Module):
    '''自编码'''

    def __init__(self):
        super(AutoEncoder, self).__init__()
        self.encoder = nn.Sequential(
            nn.Linear(28*28, 128),
            nn.Tanh(),
            nn.Linear(128, 64),
            nn.Tanh(),
            nn.Linear(64, 12),
            nn.Tanh(),
            nn.Linear(12, 3),
        )
        self.decoder = nn.Sequential(
            nn.Linear(3, 12),
            nn.Tanh(),
            nn.Linear(12, 64),
            nn.Tanh(),
            nn.Linear(64, 128),
            nn.Tanh(),
            nn.Linear(128, 28*28),
            nn.Sigmoid(),
        )

    def forward(self, x):
        encoded = self.encoder(x)
        decoded = self.decoder(encoded)
        return encoded, decoded


# 设置模型、优化器、损失函数
autoencoder = AutoEncoder()
optimizer = torch.optim.Adam(autoencoder.parameters(), lr=LR)
loss_func = nn.MSELoss()

plt.ion()

rows = 2
fig, axs = plt.subplots(
    rows, N_TEST_IMG, figsize=(N_TEST_IMG, rows))  # fig, axs

view_data = train_data.train_data[:N_TEST_IMG].view(
    -1, 28*28).type(torch.FloatTensor)/255.

for i in range(N_TEST_IMG):
    axs[0][i].imshow(np.reshape(
        view_data.data.numpy()[i], (28, 28)), cmap='gray')
    axs[0][i].set_xticks(())
    axs[0][i].set_yticks(())

# 训练并绘图
npic: int = 0
temp: int = 0

for epoch in range(EPOCHS):
    for step, (x, b_label) in enumerate(train_loader):
        b_x = x.view(-1, 28*28)
        b_y = x.view(-1, 28*28)
        encoded, decoded = autoencoder(b_x)
        loss = loss_func(decoded, b_y)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        temp = step
        if step % (BATCHS/10) == 0:
            info = "Epoch: %i | train loss: %.4f" % (epoch, loss.data.numpy())
            print(info)
            _, decoded_data = autoencoder(view_data)
            for i in range(N_TEST_IMG):
                axs[1][i].clear()
                axs[1][i].imshow(
                    np.reshape(decoded_data.data.numpy()[i], (28, 28)), cmap='gray')
                axs[1][i].set_xticks(())
                axs[1][i].set_yticks(())
            # for i in range(N_TEST_IMG):
            plt.draw()
            plt.pause(0.02)
            # npic += 1
            # plt.savefig(f'e:/temp/{int(npic)}.png')
        # if step % 100 == 0:
    # for step, (x, b_label) in enumerate(train_loader):
    print('epoch = {} temp = {}'.format(epoch, temp))
# for epoch in range(EPOCH):

plt.ioff()
plt.show()
